package org.zanata.rest.service;

import java.util.HashSet;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;

import lombok.extern.slf4j.Slf4j;

import org.hibernate.Session;
import org.jboss.resteasy.spi.NoLogWebApplicationException;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Transactional;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.security.Identity;
import org.zanata.common.LocaleId;
import org.zanata.dao.AccountDAO;
import org.zanata.dao.AccountRoleDAO;
import org.zanata.dao.LocaleDAO;
import org.zanata.model.HAccount;
import org.zanata.model.HAccountRole;
import org.zanata.model.HLocale;
import org.zanata.model.HPerson;
import org.zanata.rest.dto.Account;

@Name("accountService")
@Path(AccountResource.SERVICE_PATH)
@Slf4j
@Transactional
@Restrict("#{s:hasRole('admin')}")
public class AccountService implements AccountResource {
    /** User name that identifies an account. */
    @PathParam("username")
    String username;

    @Context
    private HttpServletRequest request;

    @Context
    private UriInfo uri;

    @In
    private AccountDAO accountDAO;

    @In
    private AccountRoleDAO accountRoleDAO;

    @In
    private LocaleDAO localeDAO;

    @In
    private Identity identity;

    @In
    private Session session;

    @Override
    public Response get() {
        log.debug("HTTP GET {}", request.getRequestURL());
        HAccount hAccount = accountDAO.getByUsername(username);
        if (hAccount == null) {
            return Response.status(Status.NOT_FOUND)
                    .entity("Username not found").build();
        }
        Account result = new Account();
        transfer(hAccount, result);

        log.debug("HTTP GET result :\n" + result);
        return Response.ok(result).build();
    }

    @Override
    public Response put(Account account) {
        log.debug("HTTP PUT {} : \n{}", request.getRequestURL(), account);

        // RestUtils.validateEntity(account);
        HAccount hAccount = accountDAO.getByUsername(username);
        ResponseBuilder response;
        String operation;

        if (hAccount == null) {
            // creating
            operation = "insert";
            response = Response.created(uri.getAbsolutePath());

            hAccount = new HAccount();
            HPerson person = new HPerson();
            person.setAccount(hAccount);
            hAccount.setPerson(person);
        } else {
            // updating
            operation = "update";
            response = Response.ok();
        }

        transfer(account, hAccount);
        // entity permission check
        identity.checkPermission(hAccount, operation);
        session.save(hAccount);
        session.flush();

        return response.build();
    }

    private void transfer(Account from, HAccount to) {
        to.setApiKey(from.getApiKey());
        to.setEnabled(from.isEnabled());
        to.setPasswordHash(from.getPasswordHash());

        HPerson hPerson = to.getPerson();
        hPerson.setEmail(from.getEmail());
        hPerson.setName(from.getName());

        to.getRoles().clear();
        for (String role : from.getRoles()) {
            HAccountRole hAccountRole = accountRoleDAO.findByName(role);
            if (hAccountRole == null) {
                // generate error for missing role
                log.debug("Invalid role '{}'", role);
                throw new NoLogWebApplicationException(Response
                        .status(Status.BAD_REQUEST)
                        .entity("Invalid role '" + role + "'").build());
            }
            to.getRoles().add(hAccountRole);
        }

        hPerson.getLanguageMemberships().clear();
        for (String tribe : from.getTribes()) {
            HLocale hTribe = localeDAO.findByLocaleId(new LocaleId(tribe));
            if (hTribe == null)
                // generate error for missing tribe
                throw new NoLogWebApplicationException(Response
                        .status(Status.BAD_REQUEST)
                        .entity("Invalid tribe '" + tribe + "'").build());
            hPerson.getLanguageMemberships().add(hTribe);
        }

        to.setUsername(from.getUsername());
    }

    private void transfer(HAccount from, Account to) {
        to.setApiKey(from.getApiKey());
        to.setEnabled(from.isEnabled());
        to.setPasswordHash(from.getPasswordHash());

        HPerson hPerson = from.getPerson();
        to.setEmail(hPerson.getEmail());
        to.setName(hPerson.getName());

        Set<String> roles = new HashSet<String>();

        for (HAccountRole role : from.getRoles()) {
            roles.add(role.getName());
        }

        to.setRoles(roles);
        to.setUsername(from.getUsername());
    }
}
